package com.hfi.command.demo02.xml;

import com.hfi.command.demo02.SmartControl;
import com.hfi.command.demo02.command.Command;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

/**
 * @author ChangLiang
 * @date 2020/4/15
 */
public class SmartControlInitializer {

    private static final String CLASS_PREFIX = "com.hfi.command.demo02.command.";

    private SmartControl smartControl;

    public SmartControlInitializer(SmartControl smartControl) {
        this.smartControl = smartControl;
    }

    public void init() {
        this.parseXml().forEach((slot) -> {
            try {
                Class<?> onCommand = Class.forName(CLASS_PREFIX + slot.getOn());
                Class<?> offCommand = Class.forName(CLASS_PREFIX + slot.getOff());
                smartControl.setCommand(
                        Integer.valueOf(slot.getId()),
                        (Command) this.reflectObject(onCommand, new HashMap<>(1)),
                        (Command) this.reflectObject(offCommand, new HashMap<>(1)));
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        });
    }

    private List<Slot> parseXml() {
        List<Slot> slots = new ArrayList<>();

        InputStream inputStream = SmartControlInitializer.class.getResourceAsStream("/commandconfig2.xml");

        SAXBuilder builder = new SAXBuilder();
        Document doc = null;
        try {
            doc = builder.build(inputStream);
        } catch (JDOMException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Element root = doc.getRootElement();
        List children = root.getChildren();
        children.forEach((child) -> {
            List attributes = ((Element) child).getAttributes();
            Map<String, String> map = new HashMap<>(attributes.size());
            attributes.forEach((attribute) -> {
                String name = ((Attribute) attribute).getName();
                String value = ((Attribute) attribute).getValue();
                map.put(name, value);
            });
            Slot slot = (Slot) this.reflectObject(Slot.class, map);
            slots.add(slot);
        });
        return slots;
    }

    private Object reflectObject(Class<?> classType, Map<String, String> map) {
        try {
            Object instance = classType.newInstance();

            map.forEach((name, value) -> {

                try {
                    String firstLetter = name.substring(0, 1).toUpperCase();
                    String setMethodName = "set" + firstLetter + name.substring(1);
                    Field field = classType.getDeclaredField(name);
                    Method setMethod = classType.getMethod(setMethodName, new Class<?>[]{field.getType()});
                    setMethod.invoke(instance, value);
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            });
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Test
    @DisplayName("test parse xml")
    public void test() {
        SmartControl smartControl = new SmartControl();
        SmartControlInitializer parser = new SmartControlInitializer(smartControl);
        List<Slot> slots = parser.parseXml();
        assertThat(slots.size()).isEqualTo(3);
    }

    @Test
    @DisplayName("test reflect object")
    public void test2() {
        SmartControl smartControl = new SmartControl();
        SmartControlInitializer parser = new SmartControlInitializer(smartControl);
        Map<String, String> map = new HashMap<>(3);
        map.put("id", "0");
        map.put("on", "DoorOnCommand");
        map.put("off", "DoorOffCommand");
        Slot slot = (Slot) parser.reflectObject(Slot.class, map);
        assertThat(slot.getOff()).isEqualTo("DoorOffCommand");
    }
}
